<?php

namespace App\Services;

use App\Exceptions\LogicException;
use App\Models\Api\SampleValue;
use App\Models\TestApi\TestApi;
use App\Models\TestApi\TestModule;
use App\Models\TestApi\TestParamRes;
use App\Models\TestApi\TestReport;
use App\Models\TestApi\TestValidatorRes;
use Yurun\Util\HttpRequest;

class RunTestService
{
    /**
     * ---测试单
     * @var TestReport
     * @type Object
     */
    public TestReport $testReport;

    public $params;

    /**
     * ---http请求类
     * @var HttpRequest
     */
    public HttpRequest $httpServe;

    public function initData(?TestReport $testReport)
    {
        $this->testReport = $testReport;
        if(!$this->testReport) {
            throw new LogicException('测试单不存在');
        }
//        if($this->testReport->status !== TestReport::STATUS_INIT) {
//            throw new LogicException('此测试单执行过了,不能再次执行');
//        }
        return $this;
    }

    public function initServe()
    {
        $this->httpServe = new HttpRequest;
        return $this;
    }

    public function runParams()
    {
        $this->params = TestParamRes::getParamsByRptUid($this->testReport->test_report_uid);
        if(empty($this->params)) {
            throw new LogicException('没有可执行用例');
        }

        foreach($this->params as $param) {
            $testApi = TestApi::find($param->test_api_id);
            if(!$testApi) {
                continue;
            }
            // TODO 取样参数和header
            $param1 = json_decode(json_encode($param->param), true);
            $headers1 = json_decode(json_encode($testApi->headers), true);
            $params = $this->getSampleValue($param1);
            $headers =  $this->getSampleValue($headers1);
            $url = $testApi->domain.$testApi->path;
            $method = $testApi->method_type;
            $res = $this->curlRun($url, $params, $headers, $method);
            $param->res = $res['res_body'];
            $param->http_code = $res['http_code'];
            $param->duration = $res['duration'];
            if($param->http_code !== 200) {
                $param->status = TestParamRes::STATUS_ERR;
                $param->save();
                // 把下面的验证器 状态 置为 1接口参数请求错误
                TestValidatorRes::where('param_res_id', $param->id)->update(['status' => TestValidatorRes::STATUS_API_ERR]);
                continue;
            } else {
                $param->status = TestParamRes::STATUS_PASS;
                $param->save();
            }


            // 取样器
            $sampleValues =  SampleValue::getBYParamID($param->param_id);
            if(empty($sampleValues?->toArray())) {
                goto TestValidator;
            }
            $biao1 = false;
            foreach($sampleValues as $sampleValue) {
                $param_res = $res['res_body']; // array
                $sample_rule = $sampleValue->sample_rule; // $.data.list.0.id
                $arr_rule = explode('.', $sample_rule);

                foreach ($arr_rule as $rule) {
                    if ($rule == '$') {
                        continue;
                    }
                    if(!isset($param_res[$rule])) {
                        //  // 0未取样 1取样成功 2取样失败
                        $sampleValue->status = SampleValue::STATUS_FAIL;
                        $sampleValue->save();
                        $biao1 = true;
                        break;
                    }
                    $param_res = $param_res[$rule];
                }

                if ($biao1) {
                    $biao1 = false;
                    continue;
                }
                $sampleValue->status = SampleValue::STATUS_SUCCESS;
                $sampleValue->sample_value = $param_res;
                $sampleValue->save();
            }

            TestValidator:
            // 验证器 验证
            $TestValidatorRes = TestValidatorRes::getByParamResId($param->id);
            if(empty($TestValidatorRes?->toArray())) {
                continue;
            }
            // 0未验证 1接口参数请求错误 2请求成功未取到验证字段 3请求成功取到验证字段验证不通过 4请求成功取到验证字段验证通过
            $validator_pass_num = 0;
            $biao = false;
            foreach($TestValidatorRes as $Validator) {
                $param_res = $res['res_body']; // array
                $validator_rule = $Validator->rule; // $.data.list.0.id
                $expect_value = $Validator->expect_value;
                if(true == strpos("_".$Validator->expect_value,'${')) { // 验证期望值 可以填 取样器的值
                    $expect_value = SampleValue::getValue($Validator->expect_value);
                }
                $arr_rule = explode('.', $validator_rule);
                foreach ($arr_rule as  $rule) {
                    if ($rule == '$') {
                        continue;
                    }
                    if(!isset($param_res[$rule])) {
                        $Validator->status = TestValidatorRes::STATUS_NOT_KEY;
                        $biao = true;
                        $Validator->save();
                        break;
                    }
                    $param_res = $param_res[$rule];
                }
                if($biao) {
                    $biao = false;
                    continue;
                }
                if($expect_value == $param_res) {
                    $Validator->status = TestValidatorRes::STATUS_SUCCESS;
                    $Validator->true_value = $param_res;
                    $Validator->save();
                    $validator_pass_num += 1;
                } else {
                    $Validator->status = TestValidatorRes::STATUS_NOT_EQUAL;
                    $Validator->true_value = $param_res;
                    $Validator->save();
                }
            }
            // 0待测试 1接口参数请求错误 2请求成功验证器未通过 3请求成功验证器也通过
            $param->success_validator_sum = $validator_pass_num;
            if($param->success_validator_sum == $param->test_validator_sum) {
                $param->status = TestParamRes::STATUS_PASS;
            } else {
                $param->status = TestParamRes::STATUS_NOT_PASS;
            }
            $param->save();
        }
        return $this;
    }

    public function done()
    {
        // 统计测试单
        $this->testReport->duration = TestParamRes::where('test_report_uid', $this->testReport->test_report_uid)->sum('duration');
        $this->testReport->success_param_sun = TestParamRes::where('test_report_uid', $this->testReport->test_report_uid)
            ->where('status', TestParamRes::STATUS_PASS)->count();
        $this->testReport->status = TestReport::STATUS_DONE;
        $this->testReport->save();

        //统计模块
        $TestModules =  TestModule::where('test_report_uid', $this->testReport->test_report_uid)->get();
        foreach($TestModules as $TestModule) {
            $TestModule->status = TestModule::STATUS_DONE;
            $TestModule->success_param_sun = TestParamRes::where("test_module_id", $TestModule->id)->where('status', TestParamRes::STATUS_PASS)->count();
            $TestModule->save();
        }

        //统计接口
        $TestApis = TestApi::where('test_report_uid', $this->testReport->test_report_uid)->get();
        foreach($TestApis as $TestApi) {
            $TestApi->success_param_sun = TestParamRes::where("test_api_id", $TestApi->id)->where('status', TestParamRes::STATUS_PASS)->count();
            if($TestApi->success_param_sun == $TestApi->test_param_sun) {
                $TestApi->status =  TestApi::STATUS_SUCCESS;
            } else {
                $TestApi->status =  TestApi::STATUS_ERROR;
            }
            $TestApi->save();
        }

        return $this;
    }

    public function curlRun(string $url, $params = [] , $headers = [], $method = 'POST')
    {
        $curl = $this->httpServe
            ->url($url)
            ->method($method)
            ->headers($headers)
            ->requestBody(json_encode($params))
            ->header('Content-Type', 'application/json;charset=UTF-8')
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', '*')
            ->header('Access-Control-Allow-Headers', '*, Content-Type, TK, Authorization, VERSION, LNG, LAT, DEVICEID, token');
        $res = $curl->send();
        $res_body = json_decode($res->getBody()->getContents(), true); // 返回结果
        $http_code = $res->getStatusCode(); // http 请求状态码
        $duration = $res->getTotalTime() * 1000; // 运行时长 单位毫秒
        return compact('http_code','res_body', 'duration');
    }

    public function getSampleValue(&$arr){
            foreach($arr as $k => &$v) {
                if(!is_array($v)) {
                    if(strpos("_".$v,'${') == true) {
                        $arr[$k] = SampleValue::getValue($v);
                    }
                } else {
                    $this->getSampleValue($v);
                }
            }
            return $arr;
    }
}
